﻿using LxBasic;
using LxLESS;
using System.Drawing;
using System.Text;

namespace LxCore
{
    internal partial class MshFileReader
    {
        StreamReader reader;
        List<FlZone> Zones;
        List<FlThread> Threads;
        FlPoint[] Points;
        public FvMesh ReadFile(string path)
        {
            //initialize
            Zones = new List<FlZone>();
            Threads = new List<FlThread>();
            sb = new StringBuilder();
            //read to fluent mesh
            using (reader = new StreamReader(path))
            {
                ReadToFluentMesh();
            }
            reader = null;
            //convert to abmesh            
            return FluentMeshToAbMesh();
        }
        #region FluentMeshToAbMesh
        protected FvMesh FluentMeshToAbMesh()
        {
            var mesh = new FvMesh();
            foreach (FlZone z in Zones)
            {
                FvZone abZone = new FvZone();
                abZone.Name = z.Name;
                abZone.InnCells = new FvInnCell[z.InnCellRange.GetCount()];
                mesh.Zones.Add(abZone);
            }
            distributeFaces(mesh);
            distributePoints(mesh);
            return mesh;
        }
        protected void distributeFaces(FvMesh mesh)
        {
            //1.先标记、计数
            foreach (var t in Threads)
            {
                var faces = t.Faces;
                //找到所在zone
                (t.OZone, int cut1) = findZone(faces[0].OSideCell);
                (t.NZone, int cut2) = findZone(faces[0].NSideCell);
                if (t.Type != "interior")//内部面不计入
                {
                    Zones[t.OZone].BouFaceCount += t.Faces.Length;
                    if (t.NZone >= 0)
                    {
                        Zones[t.NZone].BouFaceCount += t.Faces.Length;
                    }
                }
                //重新编号
                for (int i1 = 0; i1 < faces.Length; i1++)
                {
                    ref var f = ref faces[i1];
                    f.OSideCell -= cut1;
                    f.NSideCell -= cut2;
                }

            }
            (int zoneId, int cut) findZone(int cellId)
            {
                if (cellId == -1)
                {
                    return (-1, 0);
                }
                for (int i1 = 0; i1 < Zones.Count; i1++)
                {
                    if (Zones[i1].InnCellRange.Contains(cellId))
                    {

                        return (i1, Zones[i1].InnCellRange.Start);
                    }
                }
                throw new Exception($"{ToString}.findZone()");
            }
            //然后分配
            for (int i1 = 0; i1 < Zones.Count; i1++)
            {
                mesh.Zones[i1].BouFaces = new FvBouFace[Zones[i1].BouFaceCount];
            }
            foreach (var t in Threads)
            {
                var flfaces = t.Faces;
                if (t.Type == "interior")
                {
                    if (t.OZone != t.NZone) throw new Exception("非内部面");
                    if (mesh.Zones[t.OZone].InnFaces != null) throw new Exception("重复内部面");
                    var fvfaces = new FvInnFace[flfaces.Length];
                    for (int i1 = 0; i1 < flfaces.Length; i1++)
                    {
                        ref var flface = ref flfaces[i1];
                        fvfaces[i1] = new FvInnFace()
                        {
                            Nodes = flface.Nodes,
                            OSideCell = flface.OSideCell,
                            NSideCell = flface.NSideCell,
                        };
                    }
                    mesh.Zones[t.OZone].InnFaces = fvfaces;
                }
                else
                {
                    if (t.NZone < 0)//单边
                    {
                        var fvz = mesh.Zones[t.OZone];
                        int fvstart = getFilledBouFacesCount(fvz);
                        var fvfaces = fvz.BouFaces;
                        for (int i1 = 0; i1 < flfaces.Length; i1++)
                        {
                            ref var flface = ref flfaces[i1];
                            fvfaces[i1 + fvstart] = new FvBouFace()
                            {
                                Nodes = flface.Nodes,
                                OSideCell = flface.OSideCell,
                            };
                        }
                        fvz.Bounds.Add(new FvBound(fvz)
                        {
                            Name = t.Name,
                            BouFaceIdx = new IndexRange(fvstart, fvstart + flfaces.Length)
                        });
                    }
                    else
                    {
                        var ofvz = mesh.Zones[t.OZone];
                        var nfvz = mesh.Zones[t.NZone];
                        int ofvstart = getFilledBouFacesCount(ofvz);
                        int nfvstart = getFilledBouFacesCount(nfvz);
                        var ofvfaces = ofvz.BouFaces;
                        var nfvfaces = nfvz.BouFaces;
                        for (int i1 = 0; i1 < flfaces.Length; i1++)
                        {
                            ref var flface = ref flfaces[i1];
                            ofvfaces[i1 + ofvstart] = new FvBouFace()
                            {
                                Nodes = flface.Nodes,
                                OSideCell = flface.OSideCell,
                            };
                            int[] ns = new int[flface.Nodes.Length];
                            for (int i2 = 0; i2 < ns.Length; i2++)
                            {
                                ns[i2] = flface.Nodes[ns.Length - i2 - 1];
                            }
                            nfvfaces[i1 + nfvstart] = new FvBouFace()
                            {
                                Nodes = ns,
                                OSideCell = flface.NSideCell,
                            };
                        }
                        ofvz.Bounds.Add(new FvBound(ofvz)
                        {
                            Name = t.Name,
                            BouFaceIdx = new IndexRange(ofvstart, ofvstart + flfaces.Length)
                        });
                        nfvz.Bounds.Add(new FvBound(nfvz)
                        {
                            Name = t.Name,
                            BouFaceIdx = new IndexRange(nfvstart, nfvstart + flfaces.Length)
                        });
                        mesh.Joints.Add(new FvJoint(mesh)
                        {
                            OZone = ofvz,
                            OBound = ofvz.Bounds.Last(),
                            NZone = nfvz,
                            NBound = nfvz.Bounds.Last(),
                            Name = t.Name,
                        });
                    }
                }
            }
            int getFilledBouFacesCount(FvZone zone)
            {
                return (zone.Bounds.Count == 0) ? 0 : zone.Bounds.Last().FaceIdx.End;
            }
        }

        protected void distributePoints(FvMesh abmesh)
        {
            for (int i1 = 0; i1 < Points.Length; i1++)
            {
                Points[i1].SetNoUsed();
            }
            var zones = abmesh.Zones;
            for (int i1 = 0; i1 < zones.Count; i1++)
            {
                var z = zones[i1];
                //进行标记
                foreach (var f in z.InnFaces)
                {
                    makeFlag(f.Nodes);
                }
                foreach (var f in z.BouFaces)
                {
                    makeFlag(f.Nodes);
                }
                int count = newIndex();
                var innfaces = z.InnFaces;
                for (int i2 = 0; i2 < innfaces.Length; i2++)
                {
                    faceFindPoint(innfaces[i2].Nodes);
                }
                var boufaces = z.BouFaces;
                for (int i2 = 0; i2 < boufaces.Length; i2++)
                {
                    faceFindPoint(boufaces[i2].Nodes);
                }
                z.Nodes = pointsAndClear(count);
            }
            void makeFlag(int[] nodes)
            {
                foreach (var n in nodes)
                {
                    Points[n].SetUsed();
                }
            }
            int newIndex()
            {
                int newIndex = 0;
                for (int i2 = 0; i2 < Points.Length; i2++)
                {
                    ref var p = ref Points[i2];
                    if (p.GetIsUsed())
                    {
                        p.NewIndex = newIndex;
                        newIndex++;
                    }
                }
                return newIndex;
            }
            void faceFindPoint(int[] nodes)
            {
                for (int i3 = 0; i3 < nodes.Length; i3++)
                {
                    nodes[i3] = Points[nodes[i3]].NewIndex;
                }
            }
            Vector3d[] pointsAndClear(int count)
            {
                var ps = new Vector3d[count];
                for (int i2 = 0; i2 < Points.Length; i2++)
                {
                    ref var p = ref Points[i2];
                    if (p.GetIsUsed())
                    {
                        ps[p.NewIndex] = p.Point;
                        p.SetNoUsed();
                    }
                }
                return ps;
            }
        }
        #endregion

        #region ReadToFluentMesh
        protected void ReadToFluentMesh()
        {
            while (true)
            {
                while (true)
                {
                    if (reader.EndOfStream) { return; }
                    char c = (char)reader.Peek();
                    if (c == '(')
                    {
                        break;
                    }
                    else if (char.IsWhiteSpace(c))
                    {
                        reader.Read();
                        continue;
                    }
                    else
                    {
                        throw new Exception("未找到“（”");
                    }
                }
                ReadLeftBracket();
                int t = ReadInt();
                switch (t)
                {
                    case 0://描述
                        ReadUntilRightBracket();
                        continue;
                    case 2:
                        int dim = ReadInt();
                        if (dim == 2)
                        {
                            throw new Exception("不支持二维网格");
                        }
                        break;
                    case 10:
                        ReadNodes10();
                        break;
                    case 12:
                        ReadCells12();
                        break;
                    case 13:
                        ReadFaces13();
                        break;
                    case 39:
                        ReadName39();
                        break;
                    default:
                        throw new Exception($"未识别的数据类型:{t}");
                }
                ReadRightBracket();
            }
        }
        protected void ReadCells12()
        {
            ReadLeftBracket();
            int zone_id = ReadHex();
            if (zone_id == 0)
            {
                ReadUntilRightBracket();
            }
            else
            {
                int first_index = ReadHex();
                int last_index = ReadHex();
                int type = ReadInt();
                int element_type = ReadInt();
                var zone = new FlZone();
                zone.Id = zone_id;
                zone.InnCellRange = new IndexRange(first_index - 1, last_index);
                Zones.Add(zone);
                ReadRightBracket();
            }
        }
        protected void ReadFaces13()
        {
            ReadLeftBracket();
            int zone_id = ReadHex();
            if (zone_id == 0)
            {
                ReadUntilRightBracket();
            }
            else
            {
                int first_index = ReadHex();
                int last_index = ReadHex();
                int bc_type = ReadInt();//2为interior
                int face_type = ReadInt();//0mixed,2linear,3triangular,4quadrilateral,5polygonal
                ReadRightBracket();
                ReadLeftBracket();
                SkipWhiteSpace();
                //读取Face
                var faces = new FlFace[last_index - first_index + 1];
                for (int i1 = 0; i1 < faces.Length; i1++)
                {
                    string[] str = reader.ReadLine().Split();
                    ref var f = ref faces[i1];
                    f.Nodes = new int[str.Length - 2];
                    for (int i2 = 0; i2 < f.Nodes.Length; i2++)
                    {
                        f.Nodes[f.Nodes.Length - 1 - i2] = Convert.ToInt32(str[i2], 16) - 1;//fluent是左手，编号逆转序列//msh从1编号，现在从0，所以-1
                    }
                    f.OSideCell = Convert.ToInt32(str[str.Length - 2], 16) - 1;
                    f.NSideCell = Convert.ToInt32(str[str.Length - 1], 16) - 1;
                }
                var th = new FlThread();
                th.Id = zone_id;
                th.Faces = faces;
                Threads.Add(th);
                ReadRightBracket();
            }
        }
        protected void ReadNodes10()
        {
            ReadLeftBracket();
            int zone_id = ReadHex();
            if (zone_id == 0)
            {
                ReadUntilRightBracket();
            }
            else
            {
                int first_index = ReadHex();
                int last_index = ReadHex();
                int type = ReadInt();
                int dim = ReadInt();
                ReadRightBracket();
                ReadLeftBracket();
                SkipWhiteSpace();
                //读取点
                Points = new FlPoint[last_index - first_index + 1];
                for (int i1 = 0; i1 < Points.Length; i1++)
                {
                    Points[i1].Point = Vector3d.Parse(reader.ReadLine());
                }
                ReadRightBracket();
            }
        }
        protected void ReadName39()
        {
            ReadLeftBracket();
            int id = ReadInt();
            while (true)
            {
                char c = Read();
                if (c == ')')
                {
                    break;
                }
                else
                {
                    sb.Append(c);
                }
            }
            string[] ss = sb.ToString().Trim().Split(' ');
            sb.Clear();
            if (ss.Length != 2) throw new Exception("读取msh类型数据出错");
            foreach (var z in Zones)
            {
                if (z.Id == id)
                {
                    z.Type = ss[0];
                    z.Name = ss[1];
                    break;
                }
            }
            foreach (var t in Threads)
            {
                if (t.Id == id)
                {
                    t.Type = ss[0];
                    t.Name = ss[1];
                    break;
                }
            }
            ReadLeftBracket();
            ReadRightBracket();
        }
        #endregion
        #region BasicReadFunction
        protected StringBuilder sb;
        protected char Read()
        {
            return (char)reader.Read();
        }
        protected char Peek()
        {
            return (char)reader.Peek();
        }
        protected int ReadHex()
        {
            SkipWhiteSpace();
            while (true)
            {
                char c = (char)reader.Peek();
                if (c <= '9' && c >= '0' ||
                    c <= 'f' && c >= 'a')
                {
                    sb.Append(c);
                    reader.Read();
                }
                else
                {
                    string r = sb.ToString();
                    sb.Clear();
                    return Convert.ToInt32(r, 16);
                }
            }
        }
        protected int ReadInt()
        {
            SkipWhiteSpace();
            while (true)
            {
                char c = (char)reader.Peek();
                if (c <= '9' && c >= '0')
                {
                    sb.Append(c);
                    reader.Read();
                }
                else
                {
                    int r = int.Parse(sb.ToString());
                    sb.Clear();
                    return r;
                }
            }
        }
        protected double ReadDouble()
        {
            SkipWhiteSpace();
            while (true)
            {
                char c = (char)reader.Peek();
                switch (c)
                {
                    case '0':
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                    case '-':
                    case '+':
                    case '.':
                    case 'e':
                    case 'E':
                        sb.Append(c);
                        reader.Read();
                        break;
                    default:
                        double r = double.Parse(sb.ToString());
                        sb.Clear();
                        return r;
                }
            }
        }
        protected void SkipWhiteSpace()
        {
            while (char.IsWhiteSpace((char)reader.Peek()))
            {
                reader.Read();
            }
        }
        protected void ReadLeftBracket()
        {
            SkipWhiteSpace();
            if ('(' != (char)reader.Read())
            {
                throw new Exception("未找到“(");
            }
        }
        protected void ReadRightBracket()
        {
            SkipWhiteSpace();
            if (')' != (char)reader.Read())
            {
                throw new Exception("未找到“)”");
            }
        }
        protected void ReadUntilRightBracket()
        {
            while (')' != (char)reader.Read()) { }
        }
        //protected Exception GetException()
        //{
        //    return new Exception("读取msh文件出错");
        //}


        #endregion
        #region DataStruct
        protected class FlZone
        {
            public int Id;
            public string Name;
            public string Type;
            public IndexRange InnCellRange;
            public int BouFaceCount;
        }
        protected class FlThread
        {
            public int Id;
            public string Name;
            public string Type;
            public FlFace[] Faces;
            public int OZone;
            public int NZone;
        }
        protected struct FlFace
        {
            public int OSideCell;
            public int NSideCell;
            public int[] Nodes;
        }
        protected struct FlPoint
        {
            public FlPoint()
            {
            }
            public int NewIndex = -2;//-2为空
            public Vector3d Point = new Vector3d();
            public void SetUsed()
            {
                NewIndex = -1;
            }
            public void SetNoUsed()
            {
                NewIndex = -2;
            }
            public bool GetIsUsed()
            {
                return NewIndex > -2;
            }

        }
        //protected class FlLink
        //{
        //    public AbLink Link { get; set; } = new AbLink();
        //}
        #endregion 
    }

}
